﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.ServiceModel;

namespace WcfContrib.Helpers
{
    /// <summary>
    /// A helper class for dealing with communication channels
    /// </summary>
    public static class ChannelHelper
    {
        /// <summary>
        /// Returns whether the channel should be initialized.
        /// </summary>
        /// <param name="channel">The communication channel.</param>
        /// <returns></returns>
        public static bool ShouldInitializeChannel(ICommunicationObject channel)
        {
            return (channel == null
                || channel.State == CommunicationState.Closed
                || channel.State == CommunicationState.Closing
                || channel.State == CommunicationState.Faulted);
        }

        /// <summary>
        /// Returns whether the channel should be terminated.
        /// </summary>
        /// <param name="channel">The communication channel.</param>
        /// <returns></returns>
        public static bool ShouldTerminateChannel(ICommunicationObject channel)
        {
            return (channel != null
                && (channel.State == CommunicationState.Created
                || channel.State == CommunicationState.Opened
                || channel.State == CommunicationState.Faulted
                || channel.State == CommunicationState.Opening));
        }

        /// <summary>
        /// Closes the channel.
        /// </summary>
        /// <param name="channel">The communication channel.</param>
        public static void CloseChannel(ICommunicationObject channel)
        {
            if (channel != null)
            {
                try
                {
                    channel.Close();
                }
                catch
                {
                    AbortChannel(channel);
                }
            }
        }

        /// <summary>
        /// Aborts the channel.
        /// </summary>
        /// <param name="channel">The communication channel.</param>
        public static void AbortChannel(ICommunicationObject channel)
        {
            if (channel != null)
            {
                channel.Abort();
            }
        }

        /// <summary>
        /// Closes the channel asynchronously within the given timeout
        /// </summary>
        /// <param name="timeoutInSeconds">The timeout in seconds</param>
        /// <param name="channel">The communication channel</param>
        public static void CloseAsync(int timeoutInSeconds, ICommunicationObject channel)
        {
            if (channel != null)
            {
                try
                {
                    channel.BeginClose(TimeSpan.FromSeconds(timeoutInSeconds),
                        new AsyncCallback(OnBeginCloseEnded), channel);
                }
                catch
                {
                    AbortChannel(channel);
                }
            }
        }

        static void OnBeginCloseEnded(IAsyncResult state)
        {
            ICommunicationObject channel = (ICommunicationObject)state.AsyncState;

            try
            {
                channel.EndClose(state);
            }
            catch
            {
                AbortChannel(channel);
            }
        }

        /// <summary>
        /// Sets the credentials on the communication channel.
        /// </summary>
        /// <typeparam name="TContract">The type of the service contract.</typeparam>
        /// <param name="client">The client.</param>
        /// <param name="domain">The domain.</param>
        /// <param name="userName">Name of the user.</param>
        /// <param name="password">The password.</param>
        public static void SetCredentials<TContract>(ClientBase<TContract> client, string domain, string userName, string password)
            where TContract : class
        {
            client.ClientCredentials.Windows.ClientCredential = GetCredentials(domain, userName, password);
        }

        /// <summary>
        /// Sets the credentials on the communication channel.
        /// </summary>
        /// <param name="client">The client.</param>
        /// <param name="domain">The domain.</param>
        /// <param name="userName">Name of the user.</param>
        /// <param name="password">The password.</param>
        public static void SetCredentials(ChannelFactory client, string domain, string userName, string password)
        {
            client.Credentials.Windows.ClientCredential = GetCredentials(domain, userName, password);
        }

        static NetworkCredential GetCredentials(string domain, string userName, string password)
        {
            NetworkCredential credentials = new NetworkCredential();

            credentials.Domain = domain;
            credentials.UserName = userName;
            credentials.Password = password;

            return credentials;
        }
    }
}
